package com.coderman.youpinshop.stock.service.impl;

import com.coderman.youpinshop.stock.bo.StockChangeEvent;
import com.coderman.youpinshop.stock.bo.StockDDDBO;
import com.coderman.youpinshop.stock.bo.StockRecordBO;
import com.coderman.youpinshop.stock.cache.StockCacheService;
import com.coderman.youpinshop.stock.dataobject.StockDO;
import com.coderman.youpinshop.stock.enums.StockOperationEnum;
import com.coderman.youpinshop.stock.mapper.StockMapper;
import com.coderman.youpinshop.stock.mapper.StockRecordMapper;
import com.coderman.youpinshop.stock.mq.StockMqSerivce;
import com.coderman.youpinshop.stock.service.StockService;
import com.coderman.youpinshop.stock.support.SpringApplicationContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Description:
 * date: 2022/1/15
 *
 * @author shenshuai
 * @version 1.0.0
 * @since JDK 1.8
 */
@Component(value = "dDDStockService")
public class DDDStockServiceImpl implements StockService {

    @Autowired
    private StockMapper stockMapper;

    @Autowired
    private StockRecordMapper stockRecordMapper;

    @Autowired
    private StockCacheService cacheService;

    @Autowired
    private StockMqSerivce stockMqSerivce;


    @Override
    public boolean deduct(String stockCode, Integer deductCount) {
        StockDO stockDO = stockMapper.getByStockCode(stockCode);

        //在业务BO命名
        StockDDDBO stockDDDBO = StockDDDBO.convertFromDO(stockDO);
        //1. 检查库存记录是否存在
        if(stockDDDBO == null){
            return false;
        }

        //2. 重新计算库存数量,这里是区别于常规方式的一个明显标志，我们把扣减行为让stockDDDBO本身去触发，也就是说
        //这是他自己的事情，他有能力处理
        int afterQuantity = stockDDDBO.deduct(deductCount);
        //3. 构建库存变更记录

        //这里这一步可能会引起争议，如果场因为场景比较多而把大多数转换工作都放到了StockDDDBO上那么可能会导致领域模型变得混乱
        //比如混淆或者模糊了领域实体本身可以表达的事情，举个例子，我做饭了，花了一些时间，我自己知道，但是记录这件事情的可能是旁边的
        //女朋友或者说是摄像头，当然这里需要意识到如果是复杂项目就需要考虑是否用mapstruct专门构建转换层了
        StockRecordBO stockRecordBO = stockDDDBO.buildRecord(StockOperationEnum.DEDUCT, afterQuantity);
        //4. 持久化库存 和变更记录
        stockRecordMapper.insert(stockRecordBO.convertToDO());
        stockMapper.updateQuantity(stockCode,afterQuantity);
        //注意5,6两步一般来说如果没有其他因素干扰这两步可能需要在事务完成之后走异步事件来进行解耦，
        //通常来讲扣库存在真实场景下会引起数据库和缓存的不一致问题，这里不过多讨论相关细节，但是总体来说
        //扣库存的业务操作应该算领域服务里的操作，所以至于怎么具体实现跟一致性需求有关的逻辑还需要进一步构建
        //业务代码的执行流程。

        //5. 更新缓存
        cacheService.deduct(stockCode,afterQuantity);
        //6. 发送mq，这个发送mq的行为可能不会像上面那样会有更多的讨论，一般来说事情执行完之后总需要有消息的，至于
        // 异步的还是同步的，或者说是等待事务成功的都相对比较好处理，但是我们需要注意的一点是业务BO需要如何把相关上下文
        // 参数传递给Event
        stockMqSerivce.sendStockChangeMq(StockChangeEvent.builder().changeQuantity(deductCount).opearationCode(StockOperationEnum.DEDUCT.getCode()).build());
        return true;
    }
}
